package com.rpc.framework;

import com.rpc.framework.cdec.Decoder;
import com.rpc.framework.cdec.Encoder;
import com.rpc.framework.cdec.JsonDecoder;
import com.rpc.framework.cdec.JsonEncoder;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by tan on 2017/8/29.
 */
public class RpcServer {
	private ServerSocket serverSocket;
	private Map<String, Object> handlerMap;
	private Decoder decoder;
	private Encoder encoder;

	public RpcServer(int port) throws IOException {
		this.decoder = new JsonDecoder();
		this.encoder = new JsonEncoder();
		this.handlerMap = new ConcurrentHashMap<>();
		this.serverSocket = new ServerSocket(port);
		Thread thread = new Thread(new ServrSocketHandler());
		thread.start();
	}

	public void add(String handlerName, Object handler) {
		handlerMap.put(handlerName, handler);
	}

	class ServrSocketHandler implements Runnable {
		@Override
		public void run() {
			while (true) {
				try {
					Socket socket = serverSocket.accept();
					Thread thread = new Thread(new AcceptHandler(socket));
					thread.start();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	class AcceptHandler implements Runnable {
		private Socket socket;
		private OutputStream out;
		private InputStream in;


		public AcceptHandler(Socket socket) throws IOException {
			this.socket = socket;
			this.out = socket.getOutputStream();
			this.in = socket.getInputStream();
		}

		@Override
		public void run() {
			while (true) {
				try {
					byte[] readBytes = new byte[1024];
					int size = in.read(readBytes);
					if (size == 0) {
						continue;
					} else if (size == -1) {
						break;
					} else {
						ByteBuffer buffer = ByteBuffer.allocate(size);
						buffer.put(readBytes, 0, size);
						byte[] tempBytes = new byte[size];
						buffer.flip();
						buffer.get(tempBytes);
						Request request = decoder.decode(tempBytes, Request.class);
						Object object = handlerMap.get(request.getServiceName());
						Method method = object.getClass().getMethod(request.getMethodName(), request.getParameterTypes());
						Object result = method.invoke(object, request.getParameters());

						Response reponse = new Response();
						reponse.setRequestId(request.getRequestId());
						reponse.setResult(result);
						byte[] responseBytes = encoder.encode(reponse);
						out.write(responseBytes);
					}
				} catch (Exception e) {
					e.printStackTrace();
					break;
				}
			}
		}
	}
}
